package run;

import java.awt.Dimension;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import event.Event;
import event.IncomingCollisionEvent;

import processing.core.PApplet;
import processing.core.PVector;

public class Env {

	public static Env instance;
	
	private static final int padding = 10;
	private static final int NUM_CELLS = 15;
	
	List<LineCell> cells = new ArrayList<LineCell>();
	List<LineCell> cellQ = new ArrayList<LineCell>();
	List<LineCell> rmCellQ = new ArrayList<LineCell>();

	LineCell[][] occ;
	public static float xdim;
	public static float ydim;
	
	PApplet p;
	
	public Env (PApplet p) {
		this.p = p;
		
		// TODO hardcoded the size of the Env to be equivalent to the size of the screen
		// TODO will someday decouple these.
		Dimension size = p.getSize();
		xdim = (float) size.getWidth();
		ydim = (float) size.getHeight();
		occ = new LineCell[(int)xdim + padding][(int)ydim + padding];
	
		instance = this;
		for (int i = 0; i < NUM_CELLS; i++ )
			cells.add(new LineCell(
						new PVector(p.random(xdim), p.random(ydim),0),
						new PVector(p.random(-1,1), p.random(-1,1),0),
						0));
	}

	public void draw() {
		cells.addAll(cellQ);
		cells.removeAll(rmCellQ);
		cellQ.clear();
		rmCellQ.clear();
		
		int alive = 0;
		
		for (LineCell c : cells) {
			if (! c.dead) {
				c.move();
				alive++;
			}
			else {
				removeCell(c); // dramatically helps speed up the processing
			}
		}
		
		//TODO factor into a 'reporting'/analytics thread that occasionally aggregates
		// this information.
//		System.out.println("alive: " + alive +  "  dead:" + (cells.size() - alive) +
//				" TOTAL:" + cells.size());
	}
	
	void removeCell(LineCell c) {
		rmCellQ.add(c);
	}
	
	public void addCell(LineCell c) {
		if (cellQ.size() < 50)
			cellQ.add(c);
	}
	
	// if we struck another linecell, report which cell was hit.
	public LineCell isCollide(LineCell o) {
		PVector scanpt = PVector.add(o.loc, o.dir);
		
		int scanRange = 3;
		
		// TODO using a square 'radius'
		// scan a fuzzy range of 5 px
		int xlow = (int) Math.max(0, scanpt.x - scanRange);
		int xmax = (int) Math.min(xdim, scanpt.x + scanRange);
		int ylow = (int) Math.max(0, scanpt.y - scanRange);
		int ymax = (int) Math.min(ydim, scanpt.y + scanRange);
		
		for (int a = xlow; a < xmax; a++)
			for (int b = ylow; b < ymax; b++)
				if (occ[a][b] != o)
					return occ[a][b];

		return null;
	}
	
	public List<LineCell> getNearbyCells(LineCell c, float radius) {
		HashSet<LineCell> set = new HashSet<LineCell>();
		PVector scanpt = c.loc;

		// TODO using a square 'radius'
		int xlow = (int) Math.max(0, scanpt.x - radius);
		int xmax = (int) Math.min(xdim, scanpt.x + radius);
		int ylow = (int) Math.max(0, scanpt.y - radius);
		int ymax = (int) Math.min(ydim, scanpt.y + radius);
		
		for (int a = xlow; a < xmax; a++)
			for (int b = ylow; b < ymax; b++)
				if (occ[a][b] != c && (occ[a][b] != null)) {
					// DISTINCT lines, not just cell traces o shiet
					set.add( occ[a][b] );
				}

//		System.out.println(set.size());
		ArrayList<LineCell> list = new ArrayList<LineCell>();
		list.addAll(set);
		return list;
	}
	
	public void addHist(LineCell c, PVector loc) {
		// bounds checking
		if (loc.x >= 0 && loc.y >= 0)
			occ[(int)loc.x][(int)loc.y] = c;
	}

	public void send_message(LineCell collided,
			Event e) {
		//TODO optimize
		for (LineCell c : cells) {
			if (c==collided)
				c.notifyEvent(e);
		}
	}

}
